// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "fastdeploy/vision/facealign/contrib/pipnet.h"

#include "fastdeploy/vision/utils/utils.h"

namespace fastdeploy {

namespace vision {

namespace facealign {

void PIPNet::GenerateLandmarks(std::vector<FDTensor>& infer_result,
                               FaceAlignmentResult* result, float img_height,
                               float img_width) {
  FDTensor outputs_cls = infer_result.at(0);
  FDTensor outputs_x = infer_result.at(1);
  FDTensor outputs_y = infer_result.at(2);
  FDTensor outputs_nb_x = infer_result.at(3);
  FDTensor outputs_nb_y = infer_result.at(4);
  int grid_h = outputs_cls.shape[2];  // 8
  int grid_w = outputs_cls.shape[3];  // 8
  int grid_length = grid_h * grid_w;  // 8 * 8 = 64
  int input_h = size_[1];
  int input_w = size_[0];
  // fetch data from pointers
  const float* outputs_cls_ptr = static_cast<float*>(outputs_cls.Data());
  const float* outputs_x_ptr = static_cast<float*>(outputs_x.Data());
  const float* outputs_y_ptr = static_cast<float*>(outputs_y.Data());
  const float* outputs_nb_x_ptr = static_cast<float*>(outputs_nb_x.Data());
  const float* outputs_nb_y_ptr = static_cast<float*>(outputs_nb_y.Data());

  // find max_ids
  std::vector<unsigned int> max_ids(num_landmarks_);
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    const float* score_ptr = outputs_cls_ptr + i * grid_length;
    unsigned int max_id = 0;
    float max_score = score_ptr[0];
    for (unsigned int j = 0; j < grid_length; ++j) {
      if (score_ptr[j] > max_score) {
        max_score = score_ptr[j];
        max_id = j;
      }
    }
    max_ids[i] = max_id;  // range 0~64
  }
  // find x & y offsets
  std::vector<float> output_x_select(num_landmarks_);
  std::vector<float> output_y_select(num_landmarks_);
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    const float* offset_x_ptr = outputs_x_ptr + i * grid_length;
    const float* offset_y_ptr = outputs_y_ptr + i * grid_length;
    const unsigned int max_id = max_ids.at(i);
    output_x_select[i] = offset_x_ptr[max_id];
    output_y_select[i] = offset_y_ptr[max_id];
  }

  // find nb_x & nb_y offsets
  std::map<unsigned int, std::vector<float>> output_nb_x_select;
  std::map<unsigned int, std::vector<float>> output_nb_y_select;
  // initialize offsets map
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    std::vector<float> nb_x_offset(num_nb_);
    std::vector<float> nb_y_offset(num_nb_);
    output_nb_x_select[i] = nb_x_offset;
    output_nb_y_select[i] = nb_y_offset;
  }
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    for (unsigned int j = 0; j < num_nb_; ++j) {
      const unsigned int max_id = max_ids.at(i);
      const float* offset_nb_x_ptr =
          outputs_nb_x_ptr + (i * num_nb_ + j) * grid_length;
      const float* offset_nb_y_ptr =
          outputs_nb_y_ptr + (i * num_nb_ + j) * grid_length;
      output_nb_x_select[i][j] = offset_nb_x_ptr[max_id];
      output_nb_y_select[i][j] = offset_nb_y_ptr[max_id];
    }
  }

  // calculate coords
  std::vector<float> lms_pred_x(num_landmarks_);             // 19
  std::vector<float> lms_pred_y(num_landmarks_);             // 19
  std::map<unsigned int, std::vector<float>> lms_pred_nb_x;  // 19,10
  std::map<unsigned int, std::vector<float>> lms_pred_nb_y;  // 19,10

  // initialize pred maps
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    std::vector<float> nb_x_offset(num_nb_);
    std::vector<float> nb_y_offset(num_nb_);
    lms_pred_nb_x[i] = nb_x_offset;
    lms_pred_nb_y[i] = nb_y_offset;
  }
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    float cx = static_cast<float>(max_ids.at(i) % grid_w);
    float cy = static_cast<float>(max_ids.at(i) / grid_w);
    // calculate coords & normalize
    lms_pred_x[i] =
        ((cx + output_x_select[i]) * (float)net_stride_) / (float)input_w;
    lms_pred_y[i] =
        ((cy + output_y_select[i]) * (float)net_stride_) / (float)input_h;
    for (unsigned int j = 0; j < num_nb_; ++j) {
      lms_pred_nb_x[i][j] =
          ((cx + output_nb_x_select[i][j]) * (float)net_stride_) /
          (float)input_w;
      lms_pred_nb_y[i][j] =
          ((cy + output_nb_y_select[i][j]) * (float)net_stride_) /
          (float)input_h;
    }
  }

  // reverse indexes
  std::map<unsigned int, std::vector<float>>
      tmp_nb_x;  // 19,max_len_map_[num_landmarks_]
  std::map<unsigned int, std::vector<float>>
      tmp_nb_y;  // 19,max_len_map_[num_landmarks_]
  // initialize reverse maps
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    std::vector<float> tmp_x(max_len_map_[num_landmarks_]);
    std::vector<float> tmp_y(max_len_map_[num_landmarks_]);
    tmp_nb_x[i] = tmp_x;
    tmp_nb_y[i] = tmp_y;
  }
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    for (unsigned int j = 0; j < max_len_map_[num_landmarks_]; ++j) {
      unsigned int ri =
          reverse_index1_map_[num_landmarks_]
                             [i * max_len_map_[num_landmarks_] + j];
      unsigned int rj =
          reverse_index2_map_[num_landmarks_]
                             [i * max_len_map_[num_landmarks_] + j];
      tmp_nb_x[i][j] = lms_pred_nb_x[ri][rj];
      tmp_nb_y[i][j] = lms_pred_nb_y[ri][rj];
    }
  }

  // merge predictions
  result->Clear();
  for (unsigned int i = 0; i < num_landmarks_; ++i) {
    float total_x = lms_pred_x[i];
    float total_y = lms_pred_y[i];
    for (unsigned int j = 0; j < max_len_map_[num_landmarks_]; ++j) {
      total_x += tmp_nb_x[i][j];
      total_y += tmp_nb_y[i][j];
    }
    float x = total_x / ((float)max_len_map_[num_landmarks_] + 1.f);
    float y = total_y / ((float)max_len_map_[num_landmarks_] + 1.f);
    x = std::min(std::max(0.f, x), 1.0f);
    y = std::min(std::max(0.f, y), 1.0f);
    result->landmarks.emplace_back(
        std::array<float, 2>{x * img_width, y * img_height});
  }
};

void PIPNet::SetNumLandmarks(const int& num_landmarks) {
  if (std::find(supported_num_landmarks_.begin(),
                supported_num_landmarks_.end(),
                num_landmarks) == supported_num_landmarks_.end()) {
    FDWARNING << "The number of landmarks should be in {19, 29, 68, 98}."
              << std::endl;
  }
  num_landmarks_ = num_landmarks;
}
PIPNet::PIPNet(const std::string& model_file, const std::string& params_file,
               const RuntimeOption& custom_option,
               const ModelFormat& model_format) {
  if (model_format == ModelFormat::ONNX) {
    valid_cpu_backends = {Backend::OPENVINO, Backend::ORT};
    valid_gpu_backends = {Backend::ORT, Backend::TRT};
  } else {
    valid_cpu_backends = {Backend::PDINFER, Backend::ORT};
    valid_gpu_backends = {Backend::PDINFER, Backend::ORT, Backend::TRT};
  }
  runtime_option = custom_option;
  runtime_option.model_format = model_format;
  runtime_option.model_file = model_file;
  runtime_option.params_file = params_file;
  initialized = Initialize();
}

bool PIPNet::Initialize() {
  // parameters for preprocess
  size_ = {256, 256};
  mean_vals_ = {0.485f, 0.456f, 0.406f};
  std_vals_ = {0.229f, 0.224f, 0.225f};
  num_nb_ = 10;
  net_stride_ = 32;
  num_landmarks_ = 19;
  supported_num_landmarks_ = {19, 29, 68, 98};
  // parameters for num_landmarks_ == 19
  reverse_index1_map_[19] = {
      1,  2,  6,  7,  8,  1,  2,  6,  7,  8,  1,  2,  6,  7,  8,  1,  2,  6,
      0,  2,  3,  4,  6,  7,  8,  0,  2,  3,  4,  6,  7,  8,  0,  2,  3,  4,
      0,  1,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 0,  1,  3,  4,  5,  6,
      0,  1,  2,  4,  5,  6,  7,  8,  9,  10, 11, 14, 0,  1,  2,  4,  5,  6,
      1,  2,  3,  5,  9,  10, 11, 1,  2,  3,  5,  9,  10, 11, 1,  2,  3,  5,
      3,  4,  9,  10, 11, 3,  4,  9,  10, 11, 3,  4,  9,  10, 11, 3,  4,  9,
      0,  1,  2,  3,  7,  8,  12, 13, 15, 0,  1,  2,  3,  7,  8,  12, 13, 15,
      0,  1,  2,  3,  4,  5,  6,  8,  9,  10, 11, 12, 13, 15, 16, 18, 0,  1,
      0,  1,  2,  3,  4,  5,  6,  7,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18,
      0,  1,  2,  3,  4,  5,  6,  7,  8,  10, 11, 12, 13, 14, 15, 16, 17, 18,
      0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  11, 13, 14, 16, 17, 18, 0,  1,
      3,  4,  5,  9,  10, 14, 17, 3,  4,  5,  9,  10, 14, 17, 3,  4,  5,  9,
      0,  1,  6,  7,  8,  13, 14, 15, 16, 17, 18, 0,  1,  6,  7,  8,  13, 14,
      0,  2,  5,  6,  7,  8,  9,  10, 11, 12, 14, 15, 16, 17, 18, 0,  2,  5,
      4,  5,  9,  10, 11, 12, 13, 15, 16, 17, 18, 4,  5,  9,  10, 11, 12, 13,
      12, 13, 14, 16, 17, 18, 12, 13, 14, 16, 17, 18, 12, 13, 14, 16, 17, 18,
      12, 13, 14, 15, 17, 18, 12, 13, 14, 15, 17, 18, 12, 13, 14, 15, 17, 18,
      12, 13, 14, 15, 16, 18, 12, 13, 14, 15, 16, 18, 12, 13, 14, 15, 16, 18,
      15, 16, 17, 15, 16, 17, 15, 16, 17, 15, 16, 17, 15, 16, 17, 15, 16, 17};
  reverse_index2_map_[19] = {
      0, 6, 1, 4, 6, 0, 6, 1, 4, 6, 0, 6, 1, 4, 6, 0, 6, 1, 0, 1, 8, 7, 2, 2, 3,
      0, 1, 8, 7, 2, 2, 3, 0, 1, 8, 7, 3, 1, 3, 5, 5, 4, 3, 1, 5, 6, 6, 9, 3, 1,
      3, 5, 5, 4, 5, 5, 3, 1, 3, 7, 5, 5, 1, 3, 4, 9, 5, 5, 3, 1, 3, 7, 7, 8, 1,
      0, 3, 2, 2, 7, 8, 1, 0, 3, 2, 2, 7, 8, 1, 0, 6, 0, 6, 4, 1, 6, 0, 6, 4, 1,
      6, 0, 6, 4, 1, 6, 0, 6, 1, 3, 4, 9, 1, 2, 6, 9, 8, 1, 3, 4, 9, 1, 2, 6, 9,
      8, 2, 2, 2, 7, 8, 9, 0, 0, 9, 9, 9, 5, 7, 7, 8, 8, 2, 2, 4, 4, 0, 5, 6, 6,
      3, 0, 4, 5, 7, 4, 3, 8, 6, 6, 9, 6, 7, 6, 5, 0, 4, 4, 8, 6, 4, 0, 3, 8, 4,
      4, 9, 7, 6, 7, 9, 8, 7, 2, 2, 2, 9, 9, 9, 0, 0, 8, 5, 9, 7, 9, 9, 8, 4, 3,
      1, 2, 1, 6, 8, 4, 3, 1, 2, 1, 6, 8, 4, 3, 1, 2, 6, 9, 5, 7, 8, 0, 2, 1, 3,
      4, 4, 6, 9, 5, 7, 8, 0, 2, 8, 9, 8, 6, 8, 7, 7, 8, 8, 0, 0, 2, 2, 2, 5, 8,
      9, 8, 9, 7, 8, 7, 5, 2, 1, 4, 4, 1, 3, 9, 7, 8, 7, 5, 2, 1, 1, 5, 7, 0, 3,
      1, 1, 5, 7, 0, 3, 1, 1, 5, 7, 0, 3, 1, 3, 2, 3, 0, 0, 0, 3, 2, 3, 0, 0, 0,
      3, 2, 3, 0, 0, 0, 7, 6, 1, 3, 1, 2, 7, 6, 1, 3, 1, 2, 7, 6, 1, 3, 1, 2, 5,
      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5};
  max_len_map_[19] = 18;
  // parameters for num_landmarks_ == 29
  reverse_index1_map_[29] = {
      2,  4,  5,  8,  12, 13, 16, 2,  4,  5,  8,  12, 13, 16, 2,  4,  5,  8,
      12, 3,  6,  7,  9,  14, 15, 17, 3,  6,  7,  9,  14, 15, 17, 3,  6,  7,
      9,  14, 0,  3,  4,  5,  6,  7,  8,  10, 11, 12, 13, 14, 16, 0,  3,  4,
      5,  6,  7,  0,  1,  2,  4,  5,  6,  7,  9,  10, 11, 12, 14, 15, 17, 0,
      1,  2,  4,  5,  0,  2,  5,  8,  10, 12, 13, 16, 0,  2,  5,  8,  10, 12,
      13, 16, 0,  2,  5,  0,  2,  4,  8,  10, 12, 13, 16, 0,  2,  4,  8,  10,
      12, 13, 16, 0,  2,  4,  1,  3,  7,  9,  11, 14, 15, 17, 1,  3,  7,  9,
      11, 14, 15, 17, 1,  3,  7,  1,  3,  6,  9,  11, 14, 15, 17, 1,  3,  6,
      9,  11, 14, 15, 17, 1,  3,  6,  0,  2,  4,  5,  10, 12, 13, 16, 0,  2,
      4,  5,  10, 12, 13, 16, 0,  2,  4,  1,  3,  6,  7,  11, 14, 15, 17, 1,
      3,  6,  7,  11, 14, 15, 17, 1,  3,  6,  0,  2,  3,  4,  5,  8,  12, 13,
      16, 18, 20, 0,  2,  3,  4,  5,  8,  12, 13, 1,  2,  3,  6,  7,  9,  14,
      15, 17, 19, 20, 21, 1,  2,  3,  6,  7,  9,  14, 0,  2,  4,  5,  8,  10,
      13, 16, 0,  2,  4,  5,  8,  10, 13, 16, 0,  2,  4,  0,  2,  4,  5,  8,
      10, 12, 16, 18, 22, 0,  2,  4,  5,  8,  10, 12, 16, 18, 1,  3,  6,  7,
      9,  11, 15, 17, 1,  3,  6,  7,  9,  11, 15, 17, 1,  3,  6,  1,  3,  6,
      7,  9,  11, 14, 17, 19, 23, 1,  3,  6,  7,  9,  11, 14, 17, 19, 0,  2,
      4,  5,  8,  10, 12, 13, 18, 0,  2,  4,  5,  8,  10, 12, 13, 18, 0,  1,
      3,  6,  7,  9,  11, 14, 15, 19, 1,  3,  6,  7,  9,  11, 14, 15, 19, 1,
      0,  4,  5,  8,  10, 12, 13, 16, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
      0,  1,  6,  7,  9,  11, 14, 15, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27,
      28, 1,  1,  8,  9,  10, 11, 13, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25,
      26, 27, 28, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 18, 19, 20, 22, 23,
      24, 25, 26, 27, 18, 20, 21, 24, 25, 26, 27, 28, 18, 20, 21, 24, 25, 26,
      27, 28, 18, 20, 21, 19, 21, 24, 25, 26, 27, 28, 19, 21, 24, 25, 26, 27,
      28, 19, 21, 24, 25, 26, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 18, 19,
      20, 21, 22, 23, 25, 26, 27, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 18,
      19, 20, 21, 22, 23, 24, 26, 27, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28,
      18, 19, 20, 21, 22, 23, 24, 25, 27, 20, 21, 22, 23, 24, 25, 26, 28, 20,
      21, 22, 23, 24, 25, 26, 28, 20, 21, 22, 22, 23, 24, 25, 26, 27, 22, 23,
      24, 25, 26, 27, 22, 23, 24, 25, 26, 27, 22};
  reverse_index2_map_[29] = {
      9, 3, 5, 3, 7, 7, 7, 9, 3, 5, 3, 7, 7, 7, 9, 3, 5, 3, 7, 9, 3, 5, 3, 7,
      7, 7, 9, 3, 5, 3, 7, 7, 7, 9, 3, 5, 3, 7, 7, 6, 6, 6, 8, 9, 7, 0, 9, 6,
      5, 9, 6, 7, 6, 6, 6, 8, 9, 9, 7, 6, 8, 9, 6, 6, 7, 8, 0, 9, 6, 6, 6, 9,
      7, 6, 8, 9, 2, 5, 0, 5, 5, 3, 6, 5, 2, 5, 0, 5, 5, 3, 6, 5, 2, 5, 0, 1,
      3, 0, 4, 4, 2, 4, 2, 1, 3, 0, 4, 4, 2, 4, 2, 1, 3, 0, 2, 4, 0, 5, 5, 3,
      5, 5, 2, 4, 0, 5, 5, 3, 5, 5, 2, 4, 0, 1, 3, 0, 4, 4, 2, 4, 2, 1, 3, 0,
      4, 4, 2, 4, 2, 1, 3, 0, 0, 7, 4, 3, 6, 5, 3, 4, 0, 7, 4, 3, 6, 5, 3, 4,
      0, 7, 4, 0, 7, 4, 3, 6, 5, 2, 4, 0, 7, 4, 3, 6, 5, 2, 4, 0, 7, 4, 6, 0,
      8, 7, 7, 6, 4, 2, 3, 5, 6, 6, 0, 8, 7, 7, 6, 4, 2, 6, 8, 0, 7, 7, 6, 4,
      3, 3, 5, 7, 9, 6, 8, 0, 7, 7, 6, 4, 3, 1, 1, 1, 2, 3, 1, 0, 3, 1, 1, 1,
      2, 3, 1, 0, 3, 1, 1, 5, 4, 5, 4, 0, 2, 1, 1, 6, 9, 5, 4, 5, 4, 0, 2, 1,
      1, 6, 3, 1, 1, 1, 2, 3, 1, 0, 3, 1, 1, 1, 2, 3, 1, 0, 3, 1, 1, 5, 5, 5,
      4, 0, 2, 1, 1, 7, 9, 5, 5, 5, 4, 0, 2, 1, 1, 7, 4, 2, 2, 2, 1, 1, 0, 0,
      9, 4, 2, 2, 2, 1, 1, 0, 0, 9, 4, 4, 2, 2, 2, 1, 1, 0, 0, 9, 4, 2, 2, 2,
      1, 1, 0, 0, 9, 4, 8, 9, 8, 8, 7, 8, 8, 8, 8, 1, 3, 0, 8, 5, 8, 9, 9, 9,
      8, 8, 9, 8, 8, 7, 8, 8, 8, 8, 2, 4, 8, 0, 6, 7, 8, 8, 7, 8, 9, 9, 9, 9,
      8, 9, 9, 9, 9, 0, 0, 0, 6, 6, 4, 4, 6, 7, 8, 1, 1, 0, 5, 5, 2, 3, 3, 4,
      6, 1, 1, 0, 5, 5, 2, 3, 3, 4, 2, 8, 7, 7, 5, 4, 6, 5, 2, 8, 7, 7, 5, 4,
      6, 5, 2, 8, 7, 2, 8, 8, 6, 5, 5, 4, 2, 8, 8, 6, 5, 5, 4, 2, 8, 8, 6, 5,
      3, 3, 3, 1, 2, 3, 0, 2, 2, 3, 3, 3, 3, 1, 2, 3, 0, 2, 2, 4, 4, 4, 2, 1,
      1, 0, 0, 1, 2, 4, 4, 4, 2, 1, 1, 0, 0, 1, 7, 6, 5, 5, 3, 2, 1, 1, 0, 1,
      7, 6, 5, 5, 3, 2, 1, 1, 0, 9, 6, 4, 4, 3, 2, 1, 0, 9, 6, 4, 4, 3, 2, 1,
      0, 9, 6, 4, 7, 7, 9, 9, 7, 3, 7, 7, 9, 9, 7, 3, 7, 7, 9, 9, 7, 3, 7};
  max_len_map_[29] = 19;
  // parameters for num_landmarks_ == 68
  reverse_index1_map_[68] = {
      1,  2,  17, 18, 36, 1,  2,  17, 18, 36, 1,  2,  17, 18, 36, 1,  2,  17,
      18, 36, 1,  2,  0,  2,  3,  17, 0,  2,  3,  17, 0,  2,  3,  17, 0,  2,
      3,  17, 0,  2,  3,  17, 0,  2,  0,  1,  3,  4,  0,  1,  3,  4,  0,  1,
      3,  4,  0,  1,  3,  4,  0,  1,  3,  4,  0,  1,  1,  2,  4,  5,  1,  2,
      4,  5,  1,  2,  4,  5,  1,  2,  4,  5,  1,  2,  4,  5,  1,  2,  2,  3,
      5,  6,  2,  3,  5,  6,  2,  3,  5,  6,  2,  3,  5,  6,  2,  3,  5,  6,
      2,  3,  3,  4,  6,  7,  3,  4,  6,  7,  3,  4,  6,  7,  3,  4,  6,  7,
      3,  4,  6,  7,  3,  4,  3,  4,  5,  7,  8,  3,  4,  5,  7,  8,  3,  4,
      5,  7,  8,  3,  4,  5,  7,  8,  3,  4,  5,  6,  8,  9,  5,  6,  8,  9,
      5,  6,  8,  9,  5,  6,  8,  9,  5,  6,  8,  9,  5,  6,  6,  7,  9,  10,
      6,  7,  9,  10, 6,  7,  9,  10, 6,  7,  9,  10, 6,  7,  9,  10, 6,  7,
      7,  8,  10, 11, 7,  8,  10, 11, 7,  8,  10, 11, 7,  8,  10, 11, 7,  8,
      10, 11, 7,  8,  8,  9,  11, 12, 13, 8,  9,  11, 12, 13, 8,  9,  11, 12,
      13, 8,  9,  11, 12, 13, 8,  9,  9,  10, 12, 13, 9,  10, 12, 13, 9,  10,
      12, 13, 9,  10, 12, 13, 9,  10, 12, 13, 9,  10, 10, 11, 13, 14, 10, 11,
      13, 14, 10, 11, 13, 14, 10, 11, 13, 14, 10, 11, 13, 14, 10, 11, 11, 12,
      14, 15, 11, 12, 14, 15, 11, 12, 14, 15, 11, 12, 14, 15, 11, 12, 14, 15,
      11, 12, 12, 13, 15, 16, 12, 13, 15, 16, 12, 13, 15, 16, 12, 13, 15, 16,
      12, 13, 15, 16, 12, 13, 13, 14, 16, 26, 13, 14, 16, 26, 13, 14, 16, 26,
      13, 14, 16, 26, 13, 14, 16, 26, 13, 14, 14, 15, 25, 26, 45, 14, 15, 25,
      26, 45, 14, 15, 25, 26, 45, 14, 15, 25, 26, 45, 14, 15, 0,  1,  2,  18,
      19, 36, 37, 41, 0,  1,  2,  18, 19, 36, 37, 41, 0,  1,  2,  18, 19, 36,
      0,  1,  17, 19, 20, 36, 37, 38, 41, 0,  1,  17, 19, 20, 36, 37, 38, 41,
      0,  1,  17, 19, 0,  17, 18, 20, 21, 36, 37, 38, 40, 41, 0,  17, 18, 20,
      21, 36, 37, 38, 40, 41, 0,  17, 17, 18, 19, 21, 36, 37, 38, 39, 40, 41,
      17, 18, 19, 21, 36, 37, 38, 39, 40, 41, 17, 18, 18, 19, 20, 22, 27, 28,
      37, 38, 39, 40, 41, 18, 19, 20, 22, 27, 28, 37, 38, 39, 40, 41, 21, 23,
      24, 25, 27, 28, 42, 43, 44, 46, 47, 21, 23, 24, 25, 27, 28, 42, 43, 44,
      46, 47, 22, 24, 25, 26, 42, 43, 44, 45, 46, 47, 22, 24, 25, 26, 42, 43,
      44, 45, 46, 47, 22, 24, 16, 22, 23, 25, 26, 43, 44, 45, 46, 47, 16, 22,
      23, 25, 26, 43, 44, 45, 46, 47, 16, 22, 15, 16, 23, 24, 26, 43, 44, 45,
      46, 15, 16, 23, 24, 26, 43, 44, 45, 46, 15, 16, 23, 24, 14, 15, 16, 24,
      25, 44, 45, 46, 14, 15, 16, 24, 25, 44, 45, 46, 14, 15, 16, 24, 25, 44,
      20, 21, 22, 23, 28, 29, 38, 39, 40, 42, 43, 47, 20, 21, 22, 23, 28, 29,
      38, 39, 40, 42, 21, 22, 27, 29, 30, 39, 40, 42, 47, 21, 22, 27, 29, 30,
      39, 40, 42, 47, 21, 22, 27, 29, 27, 28, 30, 31, 35, 39, 42, 27, 28, 30,
      31, 35, 39, 42, 27, 28, 30, 31, 35, 39, 42, 27, 28, 29, 31, 32, 33, 34,
      35, 28, 29, 31, 32, 33, 34, 35, 28, 29, 31, 32, 33, 34, 35, 28, 2,  3,
      29, 30, 32, 33, 48, 49, 2,  3,  29, 30, 32, 33, 48, 49, 2,  3,  29, 30,
      32, 33, 29, 30, 31, 33, 34, 35, 49, 50, 29, 30, 31, 33, 34, 35, 49, 50,
      29, 30, 31, 33, 34, 35, 29, 30, 31, 32, 34, 35, 50, 51, 52, 29, 30, 31,
      32, 34, 35, 50, 51, 52, 29, 30, 31, 32, 29, 30, 31, 32, 33, 35, 52, 53,
      29, 30, 31, 32, 33, 35, 52, 53, 29, 30, 31, 32, 33, 35, 13, 14, 29, 30,
      32, 33, 34, 53, 54, 13, 14, 29, 30, 32, 33, 34, 53, 54, 13, 14, 29, 30,
      0,  1,  2,  17, 18, 19, 20, 37, 38, 39, 40, 41, 0,  1,  2,  17, 18, 19,
      20, 37, 38, 39, 0,  1,  17, 18, 19, 20, 21, 36, 38, 39, 40, 41, 0,  1,
      17, 18, 19, 20, 21, 36, 38, 39, 0,  1,  17, 18, 19, 20, 21, 27, 28, 36,
      37, 39, 40, 41, 0,  1,  17, 18, 19, 20, 21, 27, 19, 20, 21, 27, 28, 29,
      36, 37, 38, 40, 41, 19, 20, 21, 27, 28, 29, 36, 37, 38, 40, 41, 0,  1,
      17, 18, 19, 20, 21, 27, 28, 36, 37, 38, 39, 41, 0,  1,  17, 18, 19, 20,
      21, 27, 0,  1,  2,  17, 18, 19, 20, 21, 36, 37, 38, 39, 40, 0,  1,  2,
      17, 18, 19, 20, 21, 36, 22, 23, 24, 27, 28, 29, 43, 44, 45, 46, 47, 22,
      23, 24, 27, 28, 29, 43, 44, 45, 46, 47, 15, 16, 22, 23, 24, 25, 26, 27,
      42, 44, 45, 46, 47, 15, 16, 22, 23, 24, 25, 26, 27, 42, 15, 16, 22, 23,
      24, 25, 26, 42, 43, 45, 46, 47, 15, 16, 22, 23, 24, 25, 26, 42, 43, 45,
      14, 15, 16, 23, 24, 25, 26, 42, 43, 44, 46, 47, 14, 15, 16, 23, 24, 25,
      26, 42, 43, 44, 14, 15, 16, 22, 23, 24, 25, 26, 42, 43, 44, 45, 47, 14,
      15, 16, 22, 23, 24, 25, 26, 42, 15, 16, 22, 23, 24, 25, 26, 27, 28, 42,
      43, 44, 45, 46, 15, 16, 22, 23, 24, 25, 26, 27, 2,  3,  4,  5,  6,  49,
      59, 60, 2,  3,  4,  5,  6,  49, 59, 60, 2,  3,  4,  5,  6,  49, 3,  4,
      5,  31, 32, 48, 50, 51, 59, 60, 61, 67, 3,  4,  5,  31, 32, 48, 50, 51,
      59, 60, 30, 31, 32, 33, 34, 48, 49, 51, 52, 58, 59, 60, 61, 62, 66, 67,
      30, 31, 32, 33, 34, 48, 30, 31, 32, 33, 34, 35, 48, 49, 50, 52, 53, 54,
      56, 58, 60, 61, 62, 63, 64, 65, 66, 67, 30, 32, 33, 34, 35, 50, 51, 53,
      54, 55, 56, 62, 63, 64, 65, 30, 32, 33, 34, 35, 50, 51, 11, 12, 13, 34,
      35, 52, 54, 55, 63, 64, 65, 11, 12, 13, 34, 35, 52, 54, 55, 63, 64, 65,
      10, 11, 12, 13, 14, 53, 55, 64, 10, 11, 12, 13, 14, 53, 55, 64, 10, 11,
      12, 13, 14, 53, 8,  9,  10, 11, 12, 13, 53, 54, 56, 57, 63, 64, 65, 8,
      9,  10, 11, 12, 13, 53, 54, 56, 7,  8,  9,  10, 11, 12, 54, 55, 57, 58,
      63, 64, 65, 66, 7,  8,  9,  10, 11, 12, 54, 55, 6,  7,  8,  9,  10, 55,
      56, 58, 59, 62, 65, 66, 67, 6,  7,  8,  9,  10, 55, 56, 58, 59, 4,  5,
      6,  7,  8,  9,  48, 56, 57, 59, 60, 61, 62, 66, 67, 4,  5,  6,  7,  8,
      9,  48, 3,  4,  5,  6,  7,  8,  48, 49, 57, 58, 60, 61, 67, 3,  4,  5,
      6,  7,  8,  48, 49, 57, 2,  3,  4,  5,  6,  31, 48, 49, 59, 2,  3,  4,
      5,  6,  31, 48, 49, 59, 2,  3,  4,  5,  31, 32, 33, 48, 49, 50, 51, 52,
      57, 58, 59, 60, 62, 63, 66, 67, 31, 32, 33, 48, 49, 50, 33, 34, 48, 49,
      50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 63, 64, 65, 66, 67, 33,
      34, 35, 50, 51, 52, 53, 54, 55, 56, 57, 61, 62, 64, 65, 66, 34, 35, 50,
      51, 52, 53, 54, 10, 11, 12, 13, 14, 35, 53, 54, 55, 10, 11, 12, 13, 14,
      35, 53, 54, 55, 10, 11, 12, 13, 9,  10, 11, 12, 51, 52, 53, 54, 55, 56,
      57, 58, 61, 62, 63, 64, 66, 67, 9,  10, 11, 12, 7,  8,  9,  50, 51, 52,
      55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 7,  8,  9,  50, 4,  5,
      6,  7,  48, 49, 50, 51, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 4,  5,
      6,  7};
  reverse_index2_map_[68] = {
      0, 3, 1, 7, 8, 0, 3, 1, 7, 8, 0, 3, 1, 7, 8, 0, 3, 1, 7, 8, 0, 3, 1, 1, 4,
      9, 1, 1, 4, 9, 1, 1, 4, 9, 1, 1, 4, 9, 1, 1, 4, 9, 1, 1, 6, 1, 1, 5, 6, 1,
      1, 5, 6, 1, 1, 5, 6, 1, 1, 5, 6, 1, 1, 5, 6, 1, 5, 0, 0, 6, 5, 0, 0, 6, 5,
      0, 0, 6, 5, 0, 0, 6, 5, 0, 0, 6, 5, 0, 2, 0, 1, 7, 2, 0, 1, 7, 2, 0, 1, 7,
      2, 0, 1, 7, 2, 0, 1, 7, 2, 0, 2, 1, 1, 6, 2, 1, 1, 6, 2, 1, 1, 6, 2, 1, 1,
      6, 2, 1, 1, 6, 2, 1, 9, 4, 0, 1, 4, 9, 4, 0, 1, 4, 9, 4, 0, 1, 4, 9, 4, 0,
      1, 4, 9, 4, 5, 0, 1, 3, 5, 0, 1, 3, 5, 0, 1, 3, 5, 0, 1, 3, 5, 0, 1, 3, 5,
      0, 4, 0, 0, 4, 4, 0, 0, 4, 4, 0, 0, 4, 4, 0, 0, 4, 4, 0, 0, 4, 4, 0, 3, 0,
      0, 5, 3, 0, 0, 5, 3, 0, 0, 5, 3, 0, 0, 5, 3, 0, 0, 5, 3, 0, 3, 1, 0, 4, 9,
      3, 1, 0, 4, 9, 3, 1, 0, 4, 9, 3, 1, 0, 4, 9, 3, 1, 6, 1, 0, 2, 6, 1, 0, 2,
      6, 1, 0, 2, 6, 1, 0, 2, 6, 1, 0, 2, 6, 1, 7, 1, 0, 2, 7, 1, 0, 2, 7, 1, 0,
      2, 7, 1, 0, 2, 7, 1, 0, 2, 7, 1, 6, 1, 1, 4, 6, 1, 1, 4, 6, 1, 1, 4, 6, 1,
      1, 4, 6, 1, 1, 4, 6, 1, 5, 1, 0, 6, 5, 1, 0, 6, 5, 1, 0, 6, 5, 1, 0, 6, 5,
      1, 0, 6, 5, 1, 3, 0, 0, 9, 3, 0, 0, 9, 3, 0, 0, 9, 3, 0, 0, 9, 3, 0, 0, 9,
      3, 0, 3, 1, 7, 2, 8, 3, 1, 7, 2, 8, 3, 1, 7, 2, 8, 3, 1, 7, 2, 8, 3, 1, 0,
      3, 9, 0, 4, 4, 8, 6, 0, 3, 9, 0, 4, 4, 8, 6, 0, 3, 9, 0, 4, 4, 3, 8, 0, 0,
      6, 5, 7, 9, 7, 3, 8, 0, 0, 6, 5, 7, 9, 7, 3, 8, 0, 0, 7, 4, 1, 1, 6, 6, 5,
      7, 9, 5, 7, 4, 1, 1, 6, 6, 5, 7, 9, 5, 7, 4, 8, 4, 1, 0, 9, 6, 4, 7, 6, 8,
      8, 4, 1, 0, 9, 6, 4, 7, 6, 8, 8, 4, 9, 6, 0, 4, 2, 7, 9, 6, 5, 5, 9, 9, 6,
      0, 4, 2, 7, 9, 6, 5, 5, 9, 4, 1, 6, 9, 3, 8, 5, 6, 9, 9, 6, 4, 1, 6, 9, 3,
      8, 5, 6, 9, 9, 6, 0, 1, 4, 8, 7, 5, 7, 9, 8, 5, 0, 1, 4, 8, 7, 5, 7, 9, 8,
      5, 0, 1, 7, 6, 0, 1, 4, 7, 5, 6, 6, 9, 7, 6, 0, 1, 4, 7, 5, 6, 6, 9, 7, 6,
      8, 3, 5, 0, 0, 9, 6, 5, 7, 8, 3, 5, 0, 0, 9, 6, 5, 7, 8, 3, 5, 0, 8, 3, 1,
      4, 0, 8, 4, 5, 8, 3, 1, 4, 0, 8, 4, 5, 8, 3, 1, 4, 0, 8, 9, 1, 1, 9, 1, 2,
      8, 4, 7, 2, 8, 7, 9, 1, 1, 9, 1, 2, 8, 4, 7, 2, 8, 8, 0, 0, 6, 6, 8, 6, 8,
      8, 8, 0, 0, 6, 6, 8, 6, 8, 8, 8, 0, 0, 5, 0, 0, 9, 9, 9, 9, 5, 0, 0, 9, 9,
      9, 9, 5, 0, 0, 9, 9, 9, 9, 5, 4, 1, 2, 2, 2, 2, 2, 4, 1, 2, 2, 2, 2, 2, 4,
      1, 2, 2, 2, 2, 2, 4, 8, 8, 6, 5, 0, 7, 7, 9, 8, 8, 6, 5, 0, 7, 7, 9, 8, 8,
      6, 5, 0, 7, 4, 3, 0, 0, 4, 5, 8, 7, 4, 3, 0, 0, 4, 5, 8, 7, 4, 3, 0, 0, 4,
      5, 7, 2, 1, 1, 1, 1, 5, 8, 5, 7, 2, 1, 1, 1, 1, 5, 8, 5, 7, 2, 1, 1, 3, 1,
      5, 4, 1, 0, 6, 9, 3, 1, 5, 4, 1, 0, 6, 9, 3, 1, 5, 4, 1, 0, 8, 9, 5, 4, 9,
      6, 0, 8, 7, 8, 9, 5, 4, 9, 6, 0, 8, 7, 8, 9, 5, 4, 2, 2, 4, 2, 3, 5, 8, 1,
      5, 8, 4, 1, 2, 2, 4, 2, 3, 5, 8, 1, 5, 8, 5, 6, 3, 2, 2, 3, 7, 1, 1, 3, 3,
      0, 5, 6, 3, 2, 2, 3, 7, 1, 1, 3, 9, 9, 6, 6, 3, 2, 2, 7, 9, 3, 2, 1, 0, 3,
      9, 9, 6, 6, 3, 2, 2, 7, 9, 4, 3, 4, 3, 9, 7, 4, 2, 1, 4, 9, 4, 3, 4, 3, 9,
      7, 4, 2, 1, 4, 8, 7, 7, 8, 8, 5, 5, 8, 5, 2, 3, 0, 0, 2, 8, 7, 7, 8, 8, 5,
      5, 8, 4, 4, 5, 5, 5, 7, 7, 9, 0, 0, 3, 2, 2, 4, 4, 5, 5, 5, 7, 7, 9, 0, 3,
      4, 9, 1, 2, 8, 2, 4, 7, 4, 2, 3, 4, 9, 1, 2, 8, 2, 4, 7, 4, 2, 9, 9, 2, 2,
      3, 6, 6, 6, 1, 2, 3, 3, 0, 9, 9, 2, 2, 3, 6, 6, 6, 1, 6, 5, 7, 3, 2, 2, 3,
      4, 1, 1, 1, 3, 6, 5, 7, 3, 2, 2, 3, 4, 1, 1, 4, 2, 2, 8, 5, 3, 1, 8, 4, 1,
      0, 4, 4, 2, 2, 8, 5, 3, 1, 8, 4, 1, 5, 5, 4, 9, 7, 7, 5, 5, 3, 3, 0, 0, 1,
      5, 5, 4, 9, 7, 7, 5, 5, 3, 7, 8, 5, 6, 8, 8, 7, 9, 6, 0, 0, 3, 2, 2, 7, 8,
      5, 6, 8, 8, 7, 9, 6, 3, 2, 2, 5, 3, 3, 0, 6, 3, 2, 2, 5, 3, 3, 0, 6, 3, 2,
      2, 5, 3, 6, 7, 8, 4, 6, 1, 3, 9, 4, 1, 5, 8, 6, 7, 8, 4, 6, 1, 3, 9, 4, 1,
      7, 3, 3, 4, 8, 5, 1, 1, 7, 9, 8, 5, 1, 6, 9, 5, 7, 3, 3, 4, 8, 5, 9, 6, 5,
      3, 5, 6, 9, 6, 1, 1, 6, 9, 8, 8, 8, 3, 0, 3, 8, 6, 6, 6, 8, 8, 5, 3, 3, 8,
      2, 1, 5, 8, 9, 7, 1, 5, 4, 8, 8, 5, 3, 3, 8, 2, 8, 7, 6, 6, 4, 3, 1, 3, 5,
      1, 8, 8, 7, 6, 6, 4, 3, 1, 3, 5, 1, 8, 5, 2, 2, 4, 6, 2, 4, 0, 5, 2, 2, 4,
      6, 2, 4, 0, 5, 2, 2, 4, 6, 2, 7, 5, 2, 3, 6, 7, 5, 2, 2, 9, 8, 2, 5, 7, 5,
      2, 3, 6, 7, 5, 2, 2, 7, 5, 2, 3, 7, 8, 6, 0, 1, 5, 7, 6, 3, 8, 7, 5, 2, 3,
      7, 8, 6, 0, 8, 4, 2, 4, 8, 7, 0, 0, 7, 8, 7, 4, 7, 8, 4, 2, 4, 8, 7, 0, 0,
      7, 9, 7, 3, 2, 6, 7, 6, 5, 0, 0, 6, 7, 9, 7, 3, 9, 7, 3, 2, 6, 7, 6, 7, 6,
      3, 2, 5, 8, 2, 5, 8, 2, 2, 8, 4, 7, 6, 3, 2, 5, 8, 2, 5, 8, 7, 5, 3, 4, 6,
      8, 0, 0, 1, 7, 5, 3, 4, 6, 8, 0, 0, 1, 7, 5, 3, 4, 7, 7, 9, 3, 2, 0, 3, 9,
      6, 4, 5, 3, 2, 6, 3, 0, 7, 7, 9, 3, 2, 0, 8, 9, 8, 7, 2, 0, 2, 7, 8, 9, 6,
      5, 6, 9, 7, 2, 2, 7, 2, 0, 2, 8, 7, 7, 9, 4, 0, 3, 3, 5, 4, 7, 6, 3, 3, 0,
      5, 7, 7, 9, 4, 0, 3, 3, 6, 4, 3, 5, 7, 8, 0, 0, 1, 6, 4, 3, 5, 7, 8, 0, 0,
      1, 6, 4, 3, 5, 8, 9, 9, 9, 7, 4, 4, 4, 2, 1, 4, 7, 9, 5, 0, 4, 2, 9, 8, 9,
      9, 9, 9, 9, 9, 6, 5, 8, 6, 3, 2, 3, 6, 9, 4, 1, 4, 9, 1, 1, 9, 9, 9, 6, 8,
      9, 9, 8, 4, 4, 4, 6, 7, 3, 1, 2, 4, 0, 4, 9, 9, 1, 8, 9, 9, 8};
  max_len_map_[68] = 22;
  // parameters for num_landmarks_ == 98
  reverse_index1_map_[98] = {
      1,  2,  3,  4,  5,  33, 1,  2,  3,  4,  5,  33, 1,  2,  3,  4,  5,  0,
      2,  3,  4,  5,  6,  33, 0,  2,  3,  4,  5,  6,  33, 0,  2,  3,  0,  1,
      3,  4,  5,  6,  0,  1,  3,  4,  5,  6,  0,  1,  3,  4,  5,  0,  1,  2,
      4,  5,  6,  7,  0,  1,  2,  4,  5,  6,  7,  0,  1,  2,  0,  1,  2,  3,
      5,  6,  7,  8,  0,  1,  2,  3,  5,  6,  7,  8,  0,  1,  2,  3,  4,  6,
      7,  8,  9,  1,  2,  3,  4,  6,  7,  8,  9,  1,  2,  3,  4,  5,  7,  8,
      9,  10, 2,  3,  4,  5,  7,  8,  9,  10, 2,  3,  4,  5,  6,  8,  9,  10,
      3,  4,  5,  6,  8,  9,  10, 3,  4,  5,  4,  5,  6,  7,  9,  10, 11, 4,
      5,  6,  7,  9,  10, 11, 4,  5,  6,  4,  5,  6,  7,  8,  10, 11, 12, 4,
      5,  6,  7,  8,  10, 11, 12, 4,  5,  6,  7,  8,  9,  11, 12, 13, 76, 5,
      6,  7,  8,  9,  11, 12, 13, 7,  8,  9,  10, 12, 13, 14, 76, 88, 7,  8,
      9,  10, 12, 13, 14, 76, 8,  9,  10, 11, 13, 14, 15, 8,  9,  10, 11, 13,
      14, 15, 8,  9,  10, 10, 11, 12, 14, 15, 16, 10, 11, 12, 14, 15, 16, 10,
      11, 12, 14, 15, 11, 12, 13, 15, 16, 17, 11, 12, 13, 15, 16, 17, 11, 12,
      13, 15, 16, 12, 13, 14, 16, 17, 18, 12, 13, 14, 16, 17, 18, 12, 13, 14,
      16, 17, 13, 14, 15, 17, 18, 19, 13, 14, 15, 17, 18, 19, 13, 14, 15, 17,
      18, 14, 15, 16, 18, 19, 20, 14, 15, 16, 18, 19, 20, 14, 15, 16, 18, 19,
      15, 16, 17, 19, 20, 21, 15, 16, 17, 19, 20, 21, 15, 16, 17, 19, 20, 16,
      17, 18, 20, 21, 22, 16, 17, 18, 20, 21, 22, 16, 17, 18, 20, 21, 17, 18,
      19, 21, 22, 23, 24, 17, 18, 19, 21, 22, 23, 24, 17, 18, 19, 18, 19, 20,
      22, 23, 24, 25, 82, 18, 19, 20, 22, 23, 24, 25, 82, 18, 19, 20, 21, 23,
      24, 25, 26, 27, 19, 20, 21, 23, 24, 25, 26, 27, 19, 20, 21, 22, 24, 25,
      26, 27, 28, 20, 21, 22, 24, 25, 26, 27, 28, 20, 21, 22, 23, 25, 26, 27,
      28, 21, 22, 23, 25, 26, 27, 28, 21, 22, 23, 21, 22, 23, 24, 26, 27, 28,
      29, 21, 22, 23, 24, 26, 27, 28, 29, 21, 22, 23, 24, 25, 27, 28, 29, 30,
      22, 23, 24, 25, 27, 28, 29, 30, 22, 23, 24, 25, 26, 28, 29, 30, 31, 23,
      24, 25, 26, 28, 29, 30, 31, 23, 24, 25, 26, 27, 29, 30, 31, 32, 24, 25,
      26, 27, 29, 30, 31, 32, 24, 25, 26, 27, 28, 30, 31, 32, 25, 26, 27, 28,
      30, 31, 32, 25, 26, 27, 26, 27, 28, 29, 31, 32, 26, 27, 28, 29, 31, 32,
      26, 27, 28, 29, 31, 26, 27, 28, 29, 30, 32, 46, 26, 27, 28, 29, 30, 32,
      46, 26, 27, 28, 27, 28, 29, 30, 31, 46, 27, 28, 29, 30, 31, 46, 27, 28,
      29, 30, 31, 0,  1,  2,  3,  34, 41, 60, 0,  1,  2,  3,  34, 41, 60, 0,
      1,  2,  0,  33, 35, 40, 41, 60, 0,  33, 35, 40, 41, 60, 0,  33, 35, 40,
      41, 33, 34, 36, 37, 39, 40, 41, 60, 61, 62, 33, 34, 36, 37, 39, 40, 41,
      34, 35, 37, 38, 39, 40, 63, 64, 34, 35, 37, 38, 39, 40, 63, 64, 34, 36,
      38, 39, 51, 64, 36, 38, 39, 51, 64, 36, 38, 39, 51, 64, 36, 38, 36, 37,
      39, 51, 52, 63, 64, 65, 36, 37, 39, 51, 52, 63, 64, 65, 36, 35, 36, 37,
      38, 40, 62, 63, 64, 65, 66, 67, 96, 35, 36, 37, 38, 40, 33, 34, 35, 36,
      37, 38, 39, 41, 60, 61, 62, 63, 65, 66, 67, 96, 33, 0,  1,  2,  33, 34,
      35, 40, 60, 61, 67, 0,  1,  2,  33, 34, 35, 40, 43, 49, 50, 51, 68, 43,
      49, 50, 51, 68, 43, 49, 50, 51, 68, 43, 49, 42, 44, 45, 48, 49, 50, 68,
      69, 42, 44, 45, 48, 49, 50, 68, 69, 42, 42, 43, 45, 46, 47, 48, 49, 70,
      42, 43, 45, 46, 47, 48, 49, 70, 42, 32, 44, 46, 47, 48, 71, 72, 73, 32,
      44, 46, 47, 48, 71, 72, 73, 32, 29, 30, 31, 32, 45, 47, 72, 29, 30, 31,
      32, 45, 47, 72, 29, 30, 31, 30, 31, 32, 44, 45, 46, 48, 71, 72, 73, 30,
      31, 32, 44, 45, 46, 48, 42, 43, 44, 45, 46, 47, 49, 50, 69, 70, 71, 72,
      73, 74, 75, 97, 42, 42, 43, 44, 48, 50, 68, 69, 70, 74, 75, 97, 42, 43,
      44, 48, 50, 68, 42, 43, 49, 51, 52, 68, 69, 75, 42, 43, 49, 51, 52, 68,
      69, 75, 42, 37, 38, 42, 50, 52, 53, 64, 68, 37, 38, 42, 50, 52, 53, 64,
      68, 37, 51, 53, 54, 51, 53, 54, 51, 53, 54, 51, 53, 54, 51, 53, 54, 51,
      53, 51, 52, 54, 55, 56, 57, 59, 51, 52, 54, 55, 56, 57, 59, 51, 52, 54,
      52, 53, 55, 56, 57, 58, 59, 52, 53, 55, 56, 57, 58, 59, 52, 53, 55, 53,
      54, 56, 57, 76, 77, 78, 88, 53, 54, 56, 57, 76, 77, 78, 88, 53, 53, 54,
      55, 57, 58, 77, 78, 79, 88, 53, 54, 55, 57, 58, 77, 78, 79, 53, 54, 55,
      56, 58, 59, 78, 79, 80, 90, 53, 54, 55, 56, 58, 59, 78, 53, 54, 56, 57,
      59, 79, 80, 81, 82, 92, 53, 54, 56, 57, 59, 79, 80, 53, 54, 57, 58, 80,
      81, 82, 92, 53, 54, 57, 58, 80, 81, 82, 92, 53, 0,  1,  2,  3,  4,  33,
      34, 41, 61, 62, 66, 67, 96, 0,  1,  2,  3,  0,  1,  33, 34, 35, 40, 41,
      60, 62, 63, 65, 66, 67, 96, 0,  1,  33, 33, 34, 35, 36, 37, 38, 39, 40,
      41, 60, 61, 63, 64, 65, 66, 67, 96, 35, 36, 37, 38, 39, 40, 51, 52, 61,
      62, 64, 65, 66, 67, 96, 35, 36, 36, 37, 38, 39, 51, 52, 53, 63, 65, 66,
      96, 36, 37, 38, 39, 51, 52, 36, 37, 38, 39, 52, 61, 62, 63, 64, 66, 67,
      96, 36, 37, 38, 39, 52, 41, 60, 61, 62, 63, 64, 65, 67, 96, 41, 60, 61,
      62, 63, 64, 65, 67, 0,  1,  2,  3,  33, 34, 35, 40, 41, 60, 61, 62, 65,
      66, 96, 0,  1,  42, 43, 49, 50, 51, 52, 53, 69, 74, 75, 97, 42, 43, 49,
      50, 51, 52, 42, 43, 44, 48, 49, 50, 51, 68, 70, 71, 73, 74, 75, 97, 42,
      43, 44, 42, 43, 44, 45, 46, 47, 48, 49, 50, 68, 69, 71, 72, 73, 74, 75,
      97, 31, 32, 44, 45, 46, 47, 48, 69, 70, 72, 73, 74, 75, 97, 31, 32, 44,
      28, 29, 30, 31, 32, 45, 46, 47, 70, 71, 73, 74, 97, 28, 29, 30, 31, 29,
      30, 31, 32, 44, 45, 46, 47, 48, 70, 71, 72, 74, 75, 97, 29, 30, 47, 68,
      69, 70, 71, 72, 73, 75, 97, 47, 68, 69, 70, 71, 72, 73, 75, 42, 43, 49,
      50, 52, 68, 69, 70, 71, 72, 73, 74, 97, 42, 43, 49, 50, 6,  7,  8,  9,
      10, 11, 12, 55, 77, 87, 88, 89, 95, 6,  7,  8,  9,  55, 56, 76, 78, 86,
      87, 88, 89, 95, 55, 56, 76, 78, 86, 87, 88, 89, 54, 55, 56, 57, 58, 76,
      77, 79, 80, 85, 86, 87, 88, 89, 90, 94, 95, 54, 55, 56, 57, 58, 59, 77,
      78, 80, 81, 84, 85, 86, 89, 90, 91, 94, 54, 57, 58, 59, 78, 79, 81, 82,
      83, 84, 85, 90, 91, 92, 93, 94, 54, 58, 59, 80, 82, 83, 84, 91, 92, 93,
      58, 59, 80, 82, 83, 84, 91, 92, 20, 21, 22, 23, 24, 25, 26, 59, 81, 83,
      91, 92, 93, 20, 21, 22, 23, 17, 18, 19, 20, 21, 22, 23, 81, 82, 84, 91,
      92, 93, 17, 18, 19, 20, 16, 17, 18, 19, 20, 81, 82, 83, 85, 91, 92, 93,
      94, 16, 17, 18, 19, 14, 15, 16, 17, 18, 83, 84, 86, 87, 90, 93, 94, 95,
      14, 15, 16, 17, 11, 12, 13, 14, 15, 16, 76, 77, 85, 87, 88, 89, 94, 95,
      11, 12, 13, 9,  10, 11, 12, 13, 14, 76, 77, 86, 88, 89, 95, 9,  10, 11,
      12, 13, 7,  8,  9,  10, 11, 12, 13, 55, 76, 77, 86, 87, 89, 95, 7,  8,
      9,  55, 56, 76, 77, 78, 79, 86, 87, 88, 90, 95, 55, 56, 76, 77, 78, 79,
      56, 57, 58, 78, 79, 80, 83, 84, 85, 86, 87, 89, 91, 92, 93, 94, 95, 58,
      59, 79, 80, 81, 82, 83, 84, 85, 90, 92, 93, 94, 58, 59, 79, 80, 19, 20,
      21, 22, 23, 24, 25, 59, 81, 82, 83, 84, 91, 93, 19, 20, 21, 18, 19, 79,
      80, 81, 82, 83, 84, 85, 90, 91, 92, 94, 18, 19, 79, 80, 15, 16, 17, 78,
      79, 80, 83, 84, 85, 86, 87, 89, 90, 91, 93, 95, 15, 13, 14, 15, 76, 77,
      78, 85, 86, 87, 88, 89, 90, 94, 13, 14, 15, 76, 34, 35, 36, 38, 39, 40,
      41, 60, 61, 62, 63, 64, 65, 66, 67, 34, 35, 43, 44, 45, 47, 48, 49, 50,
      68, 69, 70, 71, 72, 73, 74, 75, 43, 44};
  reverse_index2_map_[98] = {
      0, 2, 4, 6, 8, 4, 0, 2, 4, 6, 8, 4, 0, 2, 4, 6, 8, 0, 0, 2, 4, 6, 8, 8, 0,
      0, 2, 4, 6, 8, 8, 0, 0, 2, 1, 1, 0, 2, 4, 6, 1, 1, 0, 2, 4, 6, 1, 1, 0, 2,
      4, 3, 2, 1, 0, 2, 4, 6, 3, 2, 1, 0, 2, 4, 6, 3, 2, 1, 6, 3, 3, 1, 0, 2, 4,
      7, 6, 3, 3, 1, 0, 2, 4, 7, 6, 6, 4, 3, 1, 0, 2, 4, 8, 6, 4, 3, 1, 0, 2, 4,
      8, 6, 7, 5, 3, 1, 0, 2, 4, 9, 7, 5, 3, 1, 0, 2, 4, 9, 7, 6, 5, 3, 1, 0, 2,
      4, 6, 5, 3, 1, 0, 2, 4, 6, 5, 3, 7, 5, 3, 1, 0, 2, 4, 7, 5, 3, 1, 0, 2, 4,
      7, 5, 3, 9, 7, 5, 3, 1, 0, 2, 5, 9, 7, 5, 3, 1, 0, 2, 5, 9, 9, 7, 5, 3, 1,
      0, 2, 5, 8, 9, 7, 5, 3, 1, 0, 2, 5, 7, 5, 3, 1, 0, 2, 5, 9, 9, 7, 5, 3, 1,
      0, 2, 5, 9, 9, 5, 3, 1, 0, 2, 4, 9, 5, 3, 1, 0, 2, 4, 9, 5, 3, 6, 3, 1, 0,
      2, 6, 6, 3, 1, 0, 2, 6, 6, 3, 1, 0, 2, 7, 3, 1, 0, 3, 7, 7, 3, 1, 0, 3, 7,
      7, 3, 1, 0, 3, 6, 3, 1, 1, 3, 6, 6, 3, 1, 1, 3, 6, 6, 3, 1, 1, 3, 7, 3, 1,
      1, 3, 7, 7, 3, 1, 1, 3, 7, 7, 3, 1, 1, 3, 6, 3, 0, 1, 3, 6, 6, 3, 0, 1, 3,
      6, 6, 3, 0, 1, 3, 7, 2, 0, 1, 3, 5, 7, 2, 0, 1, 3, 5, 7, 2, 0, 1, 3, 5, 2,
      0, 1, 3, 5, 5, 2, 0, 1, 3, 5, 5, 2, 0, 1, 3, 4, 2, 0, 1, 3, 5, 8, 4, 2, 0,
      1, 3, 5, 8, 4, 2, 0, 5, 2, 0, 1, 3, 5, 7, 9, 5, 2, 0, 1, 3, 5, 7, 9, 5, 4,
      2, 0, 1, 3, 5, 7, 9, 4, 2, 0, 1, 3, 5, 7, 9, 4, 4, 2, 0, 1, 3, 5, 7, 9, 4,
      2, 0, 1, 3, 5, 7, 9, 4, 4, 2, 0, 1, 3, 5, 7, 4, 2, 0, 1, 3, 5, 7, 4, 2, 0,
      9, 4, 2, 0, 1, 3, 5, 6, 9, 4, 2, 0, 1, 3, 5, 6, 9, 9, 4, 2, 0, 1, 3, 5, 6,
      9, 4, 2, 0, 1, 3, 5, 6, 9, 8, 4, 2, 0, 1, 3, 4, 6, 8, 4, 2, 0, 1, 3, 4, 6,
      8, 6, 4, 2, 0, 1, 3, 3, 5, 6, 4, 2, 0, 1, 3, 3, 5, 6, 6, 4, 2, 0, 1, 2, 3,
      6, 4, 2, 0, 1, 2, 3, 6, 4, 2, 6, 4, 2, 0, 1, 1, 6, 4, 2, 0, 1, 1, 6, 4, 2,
      0, 1, 8, 6, 4, 2, 0, 0, 9, 8, 6, 4, 2, 0, 0, 9, 8, 6, 4, 8, 6, 4, 2, 0, 6,
      8, 6, 4, 2, 0, 6, 8, 6, 4, 2, 0, 2, 4, 5, 8, 3, 1, 6, 2, 4, 5, 8, 3, 1, 6,
      2, 4, 5, 7, 1, 1, 5, 0, 8, 7, 1, 1, 5, 0, 8, 7, 1, 1, 5, 0, 7, 1, 2, 8, 6,
      0, 5, 9, 8, 8, 7, 1, 2, 8, 6, 0, 5, 8, 2, 1, 4, 0, 6, 7, 9, 8, 2, 1, 4, 0,
      6, 7, 9, 8, 1, 0, 5, 5, 7, 1, 0, 5, 5, 7, 1, 0, 5, 5, 7, 1, 0, 4, 0, 2, 2,
      6, 6, 2, 8, 4, 0, 2, 2, 6, 6, 2, 8, 4, 4, 0, 2, 1, 4, 7, 4, 4, 5, 9, 9, 7,
      4, 0, 2, 1, 4, 5, 2, 0, 3, 9, 9, 4, 2, 7, 5, 4, 8, 9, 8, 6, 6, 5, 5, 7, 9,
      0, 0, 3, 3, 2, 6, 7, 5, 7, 9, 0, 0, 3, 3, 2, 5, 0, 6, 7, 2, 5, 0, 6, 7, 2,
      5, 0, 6, 7, 2, 5, 1, 1, 8, 5, 0, 4, 9, 7, 1, 1, 8, 5, 0, 4, 9, 7, 1, 8, 1,
      1, 7, 4, 0, 6, 9, 8, 1, 1, 7, 4, 0, 6, 9, 8, 7, 2, 1, 0, 6, 9, 8, 9, 7, 2,
      1, 0, 6, 9, 8, 9, 7, 8, 5, 4, 2, 2, 1, 6, 8, 5, 4, 2, 2, 1, 6, 8, 5, 4, 9,
      7, 6, 3, 0, 0, 3, 6, 2, 7, 9, 7, 6, 3, 0, 0, 3, 7, 3, 0, 3, 5, 2, 2, 9, 8,
      4, 5, 7, 6, 7, 9, 6, 7, 2, 0, 4, 2, 1, 3, 2, 7, 9, 5, 8, 2, 0, 4, 2, 1, 3,
      0, 4, 3, 1, 5, 2, 6, 8, 0, 4, 3, 1, 5, 2, 6, 8, 0, 5, 6, 5, 5, 1, 5, 8, 8,
      5, 6, 5, 5, 1, 5, 8, 8, 5, 0, 1, 9, 0, 1, 9, 0, 1, 9, 0, 1, 9, 0, 1, 9, 0,
      1, 7, 0, 1, 9, 9, 9, 9, 7, 0, 1, 9, 9, 9, 9, 7, 0, 1, 4, 0, 5, 2, 0, 2, 4,
      4, 0, 5, 2, 0, 2, 4, 4, 0, 5, 6, 5, 0, 8, 6, 6, 9, 6, 6, 5, 0, 8, 6, 6, 9,
      6, 6, 3, 2, 0, 2, 7, 7, 5, 7, 8, 3, 2, 0, 2, 7, 7, 5, 7, 2, 0, 2, 1, 1, 2,
      4, 3, 5, 7, 2, 0, 2, 1, 1, 2, 4, 4, 3, 7, 1, 0, 5, 4, 8, 8, 8, 4, 3, 7, 1,
      0, 5, 4, 7, 4, 7, 0, 9, 6, 6, 6, 7, 4, 7, 0, 9, 6, 6, 6, 7, 4, 5, 6, 7, 8,
      2, 5, 4, 1, 9, 6, 1, 9, 4, 5, 6, 7, 8, 9, 3, 4, 6, 2, 3, 1, 2, 9, 7, 4, 0,
      5, 8, 9, 3, 9, 6, 5, 6, 7, 7, 3, 1, 7, 4, 2, 3, 6, 4, 1, 4, 0, 8, 5, 3, 3,
      1, 8, 8, 9, 7, 3, 1, 0, 5, 8, 3, 8, 5, 8, 4, 2, 8, 4, 3, 9, 1, 1, 7, 8, 8,
      4, 2, 8, 4, 3, 9, 6, 5, 9, 7, 9, 6, 0, 0, 3, 5, 2, 9, 6, 5, 9, 7, 9, 3, 4,
      1, 5, 5, 3, 2, 1, 9, 3, 4, 1, 5, 5, 3, 2, 9, 8, 8, 9, 6, 7, 9, 9, 6, 0, 0,
      5, 6, 2, 4, 9, 8, 4, 8, 8, 2, 3, 2, 8, 1, 8, 1, 9, 4, 8, 8, 2, 3, 2, 3, 5,
      8, 8, 1, 3, 9, 0, 3, 7, 8, 5, 0, 5, 3, 5, 8, 9, 6, 5, 6, 8, 6, 1, 4, 7, 6,
      4, 2, 5, 4, 2, 4, 0, 9, 8, 6, 4, 3, 3, 4, 9, 1, 1, 0, 4, 7, 2, 9, 8, 6, 8,
      7, 7, 5, 4, 5, 2, 5, 8, 1, 1, 6, 7, 8, 7, 7, 5, 9, 8, 8, 9, 9, 7, 4, 7, 9,
      5, 0, 0, 1, 6, 3, 9, 8, 9, 5, 5, 2, 4, 3, 2, 3, 1, 9, 5, 5, 2, 4, 3, 2, 3,
      6, 9, 9, 6, 8, 1, 0, 6, 8, 9, 5, 3, 4, 6, 9, 9, 6, 9, 8, 6, 6, 5, 6, 7, 8,
      4, 2, 0, 8, 7, 9, 8, 6, 6, 1, 5, 2, 7, 5, 3, 2, 0, 3, 1, 5, 2, 7, 5, 3, 2,
      0, 7, 4, 3, 4, 9, 7, 5, 1, 3, 7, 7, 6, 7, 2, 2, 3, 4, 6, 7, 4, 3, 4, 6, 9,
      0, 0, 9, 9, 6, 9, 7, 0, 7, 2, 8, 5, 3, 3, 3, 2, 5, 7, 6, 7, 8, 3, 2, 7, 4,
      4, 8, 5, 1, 6, 2, 3, 5, 0, 2, 3, 5, 1, 6, 2, 3, 5, 0, 2, 7, 6, 6, 6, 7, 8,
      9, 8, 4, 2, 8, 0, 8, 7, 6, 6, 6, 8, 7, 6, 5, 7, 8, 9, 3, 1, 1, 3, 1, 2, 8,
      7, 6, 5, 7, 5, 4, 5, 9, 7, 5, 5, 1, 4, 5, 1, 5, 7, 5, 4, 5, 8, 5, 4, 6, 8,
      8, 2, 2, 8, 4, 9, 0, 9, 8, 5, 4, 6, 9, 8, 4, 4, 6, 8, 5, 8, 2, 5, 5, 4, 6,
      1, 9, 8, 4, 9, 8, 5, 4, 6, 7, 1, 3, 1, 1, 3, 2, 9, 8, 5, 4, 6, 9, 8, 7, 7,
      8, 9, 9, 6, 0, 2, 8, 1, 5, 5, 9, 8, 7, 3, 6, 3, 0, 2, 8, 3, 4, 3, 6, 0, 3,
      6, 3, 0, 2, 8, 8, 6, 8, 1, 0, 1, 9, 6, 3, 6, 9, 6, 6, 9, 7, 1, 8, 6, 5, 6,
      2, 0, 3, 4, 3, 9, 5, 3, 0, 9, 6, 5, 6, 2, 9, 8, 8, 7, 7, 9, 9, 7, 2, 0, 1,
      8, 5, 5, 9, 8, 8, 9, 8, 9, 8, 1, 4, 0, 0, 4, 8, 1, 4, 7, 9, 8, 9, 8, 8, 9,
      9, 6, 4, 7, 7, 4, 0, 4, 7, 9, 1, 9, 6, 6, 8, 8, 9, 9, 4, 1, 8, 5, 0, 0, 4,
      1, 9, 8, 8, 9, 9, 4, 9, 7, 7, 8, 7, 7, 8, 5, 3, 0, 2, 3, 2, 0, 3, 9, 7, 7,
      7, 9, 8, 7, 7, 8, 4, 3, 0, 3, 4, 3, 0, 2, 7, 7};
  max_len_map_[98] = 17;
  if (!InitRuntime()) {
    FDERROR << "Failed to initialize fastdeploy backend." << std::endl;
    return false;
  }
  return true;
}

bool PIPNet::Preprocess(Mat* mat, FDTensor* output,
                        std::map<std::string, std::array<int, 2>>* im_info) {
  // Resize
  int resize_w = size_[0];
  int resize_h = size_[1];
  if (resize_h != mat->Height() || resize_w != mat->Width()) {
    Resize::Run(mat, resize_w, resize_h);
  }
  // RGR2RGB
  BGR2RGB::Run(mat);

  // Normalize
  Normalize::Run(mat, mean_vals_, std_vals_);

  // Record output shape of preprocessed image
  (*im_info)["output_shape"] = {mat->Height(), mat->Width()};

  HWC2CHW::Run(mat);
  Cast::Run(mat, "float");
  mat->ShareWithTensor(output);
  output->shape.insert(output->shape.begin(), 1);  // reshape to n, c, h, w
  return true;
}

bool PIPNet::Postprocess(
    std::vector<FDTensor>& infer_result, FaceAlignmentResult* result,
    const std::map<std::string, std::array<int, 2>>& im_info) {
  FDASSERT(infer_result.at(0).shape[0] == 1, "Only support batch = 1 now.");
  if (infer_result.at(0).dtype != FDDataType::FP32) {
    FDERROR << "Only support post process with float32 data." << std::endl;
    return false;
  }

  auto iter_in = im_info.find("input_shape");
  FDASSERT(iter_in != im_info.end(), "Cannot find input_shape from im_info.");
  int in_h = iter_in->second[0];
  int in_w = iter_in->second[1];
  GenerateLandmarks(infer_result, result, in_h, in_w);

  return true;
}

bool PIPNet::Predict(cv::Mat* im, FaceAlignmentResult* result) {
  Mat mat(*im);
  std::vector<FDTensor> input_tensors(1);

  std::map<std::string, std::array<int, 2>> im_info;

  // Record the shape of image and the shape of preprocessed image
  im_info["input_shape"] = {mat.Height(), mat.Width()};
  im_info["output_shape"] = {mat.Height(), mat.Width()};

  if (!Preprocess(&mat, &input_tensors[0], &im_info)) {
    FDERROR << "Failed to preprocess input image." << std::endl;
    return false;
  }
  input_tensors[0].name = InputInfoOfRuntime(0).name;
  std::vector<FDTensor> output_tensors;
  if (!Infer(input_tensors, &output_tensors)) {
    FDERROR << "Failed to inference." << std::endl;
    return false;
  }

  if (!Postprocess(output_tensors, result, im_info)) {
    FDERROR << "Failed to post process." << std::endl;
    return false;
  }
  return true;
}

}  // namespace facealign
}  // namespace vision
}  // namespace fastdeploy